home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2002 #11 / Amiga Plus CD - 2002 - No. 11.iso / Tools / Development / ncurses-5.3 / ncurses / base / lib_getch.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-10-27  |  13.7 KB  |  538 lines

  1. /****************************************************************************
  2.  * Copyright (c) 1998-2001,2002 Free Software Foundation, Inc.              *
  3.  *                                                                          *
  4.  * Permission is hereby granted, free of charge, to any person obtaining a  *
  5.  * copy of this software and associated documentation files (the            *
  6.  * "Software"), to deal in the Software without restriction, including      *
  7.  * without limitation the rights to use, copy, modify, merge, publish,      *
  8.  * distribute, distribute with modifications, sublicense, and/or sell       *
  9.  * copies of the Software, and to permit persons to whom the Software is    *
  10.  * furnished to do so, subject to the following conditions:                 *
  11.  *                                                                          *
  12.  * The above copyright notice and this permission notice shall be included  *
  13.  * in all copies or substantial portions of the Software.                   *
  14.  *                                                                          *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
  16.  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
  17.  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
  18.  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
  19.  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
  20.  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
  21.  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
  22.  *                                                                          *
  23.  * Except as contained in this notice, the name(s) of the above copyright   *
  24.  * holders shall not be used in advertising or otherwise to promote the     *
  25.  * sale, use or other dealings in this Software without prior written       *
  26.  * authorization.                                                           *
  27.  ****************************************************************************/
  28.  
  29. /****************************************************************************
  30.  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
  31.  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
  32.  ****************************************************************************/
  33.  
  34. /*
  35. **    lib_getch.c
  36. **
  37. **    The routine getch().
  38. **
  39. */
  40.  
  41. #include <curses.priv.h>
  42.  
  43. MODULE_ID("$Id: lib_getch.c,v 1.67 2002/09/07 17:17:59 tom Exp $")
  44.  
  45. #include <fifo_defs.h>
  46.  
  47. NCURSES_EXPORT_VAR(int)
  48. ESCDELAY = 1000;        /* max interval betw. chars in funkeys, in millisecs */
  49.  
  50. #ifdef NCURSES_WGETCH_EVENTS
  51. #define TWAIT_MASK 7
  52. #else
  53. #define TWAIT_MASK 3
  54. #endif
  55.  
  56. static inline int
  57. fifo_peek(void)
  58. {
  59.     int ch = SP->_fifo[peek];
  60.     TR(TRACE_IEVENT, ("peeking at %d", peek));
  61.  
  62.     p_inc();
  63.     return ch;
  64. }
  65.  
  66. static inline int
  67. fifo_pull(void)
  68. {
  69.     int ch;
  70.     ch = SP->_fifo[head];
  71.     TR(TRACE_IEVENT, ("pulling %s from %d", _tracechar(ch), head));
  72.  
  73.     if (peek == head) {
  74.     h_inc();
  75.     peek = head;
  76.     } else
  77.     h_inc();
  78.  
  79. #ifdef TRACE
  80.     if (_nc_tracing & TRACE_IEVENT)
  81.     _nc_fifo_dump();
  82. #endif
  83.     return ch;
  84. }
  85.  
  86. static inline int
  87. fifo_push(EVENTLIST_0th(_nc_eventlist * evl))
  88. {
  89.     int n;
  90.     int ch = 0;
  91.     int mask;
  92.  
  93.     (void) mask;
  94.     if (tail == -1)
  95.     return ERR;
  96.  
  97. #ifdef HIDE_EINTR
  98.   again:
  99.     errno = 0;
  100. #endif
  101.  
  102. #ifdef NCURSES_WGETCH_EVENTS
  103.     if (evl
  104. #if USE_GPM_SUPPORT || defined(USE_EMX_MOUSE)
  105.     || (SP->_mouse_fd >= 0)
  106. #endif
  107.     ) {
  108.     mask = _nc_timed_wait(TWAIT_MASK, -1, (int *) 0, evl);
  109.     } else
  110.     mask = 0;
  111.  
  112.     if (mask & 4) {
  113.     T(("fifo_push: ungetch KEY_EVENT"));
  114.     ungetch(KEY_EVENT);
  115.     return KEY_EVENT;
  116.     }
  117. #elif USE_GPM_SUPPORT || defined(USE_EMX_MOUSE)
  118.     if (SP->_mouse_fd >= 0)
  119.     mask = _nc_timed_wait(TWAIT_MASK, -1, (int *) 0 EVENTLIST_2nd(evl));
  120. #endif
  121.  
  122. #if USE_GPM_SUPPORT || defined(USE_EMX_MOUSE)
  123.     if ((SP->_mouse_fd >= 0) && (mask & 2)) {
  124.     SP->_mouse_event(SP);
  125.     ch = KEY_MOUSE;
  126.     n = 1;
  127.     } else
  128. #endif
  129.     {                /* Can block... */
  130.     unsigned char c2 = 0;
  131.     n = read(SP->_ifd, &c2, 1);
  132.     ch = c2;
  133.     }
  134.  
  135. #ifdef HIDE_EINTR
  136.     /*
  137.      * Under System V curses with non-restarting signals, getch() returns
  138.      * with value ERR when a handled signal keeps it from completing.
  139.      * If signals restart system calls, OTOH, the signal is invisible
  140.      * except to its handler.
  141.      *
  142.      * We don't want this difference to show.  This piece of code
  143.      * tries to make it look like we always have restarting signals.
  144.      */
  145.     if (n <= 0 && errno == EINTR)
  146.     goto again;
  147. #endif
  148.  
  149.     if ((n == -1) || (n == 0)) {
  150.     TR(TRACE_IEVENT, ("read(%d,&ch,1)=%d, errno=%d", SP->_ifd, n, errno));
  151.     ch = ERR;
  152.     }
  153.     TR(TRACE_IEVENT, ("read %d characters", n));
  154.  
  155.     SP->_fifo[tail] = ch;
  156.     SP->_fifohold = 0;
  157.     if (head == -1)
  158.     head = peek = tail;
  159.     t_inc();
  160.     TR(TRACE_IEVENT, ("pushed %s at %d", _tracechar(ch), tail));
  161. #ifdef TRACE
  162.     if (_nc_tracing & TRACE_IEVENT)
  163.     _nc_fifo_dump();
  164. #endif
  165.     return ch;
  166. }
  167.  
  168. static inline void
  169. fifo_clear(void)
  170. {
  171.     memset(SP->_fifo, 0, sizeof(SP->_fifo));
  172.     head = -1;
  173.     tail = peek = 0;
  174. }
  175.  
  176. static int kgetch(EVENTLIST_0th(_nc_eventlist * evl));
  177.  
  178. #define wgetch_should_refresh(win) (\
  179.     (is_wintouched(win) || (win->_flags & _HASMOVED)) \
  180.     && !(win->_flags & _ISPAD))
  181.  
  182. NCURSES_EXPORT(int)
  183. _nc_wgetch(WINDOW *win,
  184.        unsigned long *result,
  185.        int use_meta
  186.        EVENTLIST_2nd(_nc_eventlist * evl))
  187. {
  188.     int ch;
  189. #ifdef NCURSES_WGETCH_EVENTS
  190.     long event_delay = -1;
  191. #endif
  192.  
  193.     T((T_CALLED("_nc_wgetch(%p)"), win));
  194.  
  195.     *result = 0;
  196.     if (!win)
  197.     returnCode(ERR);
  198.  
  199.     if (cooked_key_in_fifo()) {
  200.     if (wgetch_should_refresh(win))
  201.         wrefresh(win);
  202.  
  203.     *result = fifo_pull();
  204.     returnCode(OK);
  205.     }
  206. #ifdef NCURSES_WGETCH_EVENTS
  207.     if (evl && (evl->count == 0))
  208.     evl = NULL;
  209.     event_delay = _nc_eventlist_timeout(evl);
  210. #endif
  211.  
  212.     /*
  213.      * Handle cooked mode.  Grab a string from the screen,
  214.      * stuff its contents in the FIFO queue, and pop off
  215.      * the first character to return it.
  216.      */
  217.     if (head == -1 && !SP->_raw && !SP->_cbreak) {
  218.     char buf[MAXCOLUMNS], *sp;
  219.     int rc;
  220.  
  221.     TR(TRACE_IEVENT, ("filling queue in cooked mode"));
  222.  
  223.     rc = wgetnstr(win, buf, MAXCOLUMNS);
  224.  
  225.     /* ungetch in reverse order */
  226. #ifdef NCURSES_WGETCH_EVENTS
  227.     if (rc != KEY_EVENT)
  228. #endif
  229.         ungetch('\n');
  230.     for (sp = buf + strlen(buf); sp > buf; sp--)
  231.         ungetch(sp[-1]);
  232.  
  233. #ifdef NCURSES_WGETCH_EVENTS
  234.     /* Return it first */
  235.     if (rc == KEY_EVENT) {
  236.         *result = rc;
  237.         returnCode(OK);
  238.     }
  239. #endif
  240.  
  241.     *result = fifo_pull();
  242.     returnCode(OK);
  243.     }
  244.  
  245.     if (win->_use_keypad != SP->_keypad_on)
  246.     _nc_keypad(win->_use_keypad);
  247.  
  248.     if (wgetch_should_refresh(win))
  249.     wrefresh(win);
  250.  
  251.     if (!win->_notimeout && (win->_delay >= 0 || SP->_cbreak > 1)) {
  252.     int delay;
  253.  
  254.     TR(TRACE_IEVENT, ("timed delay in wgetch()"));
  255.     if (SP->_cbreak > 1)
  256.         delay = (SP->_cbreak - 1) * 100;
  257.     else
  258.         delay = win->_delay;
  259.  
  260. #ifdef NCURSES_WGETCH_EVENTS
  261.     if (event_delay >= 0 && delay > event_delay)
  262.         delay = event_delay;
  263. #endif
  264.  
  265.     TR(TRACE_IEVENT, ("delay is %d milliseconds", delay));
  266.  
  267.     if (head == -1) {    /* fifo is empty */
  268.         int rc = _nc_timed_wait(TWAIT_MASK,
  269.                     delay,
  270.                     (int *) 0
  271.                     EVENTLIST_2nd(evl));
  272.  
  273. #ifdef NCURSES_WGETCH_EVENTS
  274.         if (rc & 4) {
  275.         *result = KEY_EVENT;
  276.         returnCode(OK);
  277.         }
  278. #endif
  279.         if (!rc)
  280.         returnCode(ERR);
  281.     }
  282.     /* else go on to read data available */
  283.     }
  284.  
  285.     if (win->_use_keypad) {
  286.     /*
  287.      * This is tricky.  We only want to get special-key
  288.      * events one at a time.  But we want to accumulate
  289.      * mouse events until either (a) the mouse logic tells
  290.      * us it's picked up a complete gesture, or (b)
  291.      * there's a detectable time lapse after one.
  292.      *
  293.      * Note: if the mouse code starts failing to compose
  294.      * press/release events into clicks, you should probably
  295.      * increase the wait with mouseinterval().
  296.      */
  297.     int runcount = 0;
  298.     int rc;
  299.  
  300.     do {
  301.         ch = kgetch(EVENTLIST_1st(evl));
  302.         if (ch == KEY_MOUSE) {
  303.         ++runcount;
  304.         if (SP->_mouse_inline(SP))
  305.             break;
  306.         }
  307.         if (SP->_maxclick < 0)
  308.         break;
  309.     } while
  310.         (ch == KEY_MOUSE
  311.          && (((rc = _nc_timed_wait(TWAIT_MASK,
  312.                        SP->_maxclick,
  313.                        (int *) 0
  314.                        EVENTLIST_2nd(evl))) != 0
  315.           && !(rc & 4))
  316.          || !SP->_mouse_parse(runcount)));
  317. #ifdef NCURSES_WGETCH_EVENTS
  318.     if ((rc & 4) && !ch == KEY_EVENT) {
  319.         ungetch(ch);
  320.         ch = KEY_EVENT;
  321.     }
  322. #endif
  323.     if (runcount > 0 && ch != KEY_MOUSE) {
  324. #ifdef NCURSES_WGETCH_EVENTS
  325.         /* mouse event sequence ended by an event, report event */
  326.         if (ch == KEY_EVENT) {
  327.         ungetch(KEY_MOUSE);    /* FIXME This interrupts a gesture... */
  328.         } else
  329. #endif
  330.         {
  331.         /* mouse event sequence ended by keystroke, store keystroke */
  332.         ungetch(ch);
  333.         ch = KEY_MOUSE;
  334.         }
  335.     }
  336.     } else {
  337.     if (head == -1)
  338.         fifo_push(EVENTLIST_1st(evl));
  339.     ch = fifo_pull();
  340.     }
  341.  
  342.     if (ch == ERR) {
  343. #if USE_SIZECHANGE
  344.     if (SP->_sig_winch) {
  345.         _nc_update_screensize();
  346.         /* resizeterm can push KEY_RESIZE */
  347.         if (cooked_key_in_fifo()) {
  348.         *result = fifo_pull();
  349.         returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK);
  350.         }
  351.     }
  352. #endif
  353.     returnCode(ERR);
  354.     }
  355.  
  356.     /*
  357.      * If echo() is in effect, display the printable version of the
  358.      * key on the screen.  Carriage return and backspace are treated
  359.      * specially by Solaris curses:
  360.      *
  361.      * If carriage return is defined as a function key in the
  362.      * terminfo, e.g., kent, then Solaris may return either ^J (or ^M
  363.      * if nonl() is set) or KEY_ENTER depending on the echo() mode. 
  364.      * We echo before translating carriage return based on nonl(),
  365.      * since the visual result simply moves the cursor to column 0.
  366.      *
  367.      * Backspace is a different matter.  Solaris curses does not
  368.      * translate it to KEY_BACKSPACE if kbs=^H.  This does not depend
  369.      * on the stty modes, but appears to be a hardcoded special case.
  370.      * This is a difference from ncurses, which uses the terminfo entry.
  371.      * However, we provide the same visual result as Solaris, moving the
  372.      * cursor to the left.
  373.      */
  374.     if (SP->_echo && !(win->_flags & _ISPAD)) {
  375.     chtype backup = (ch == KEY_BACKSPACE) ? '\b' : ch;
  376.     if (backup < KEY_MIN)
  377.         wechochar(win, backup);
  378.     }
  379.  
  380.     /*
  381.      * Simulate ICRNL mode
  382.      */
  383.     if ((ch == '\r') && SP->_nl)
  384.     ch = '\n';
  385.  
  386.     /* Strip 8th-bit if so desired.  We do this only for characters that
  387.      * are in the range 128-255, to provide compatibility with terminals
  388.      * that display only 7-bit characters.  Note that 'ch' may be a
  389.      * function key at this point, so we mustn't strip _those_.
  390.      */
  391.     if (!use_meta)
  392.     if ((ch < KEY_MIN) && (ch & 0x80))
  393.         ch &= 0x7f;
  394.  
  395.     T(("wgetch returning : %s", _tracechar(ch)));
  396.  
  397.     *result = ch;
  398.     returnCode(ch >= KEY_MIN ? KEY_CODE_YES : OK);
  399. }
  400.  
  401. #ifdef NCURSES_WGETCH_EVENTS
  402. NCURSES_EXPORT(int)
  403. wgetch_events(WINDOW *win, _nc_eventlist * evl)
  404. {
  405.     int code;
  406.     unsigned long value;
  407.  
  408.     T((T_CALLED("wgetch_events(%p,%p)"), win, evl));
  409.     code = _nc_wgetch(win,
  410.               &value,
  411.               SP->_use_meta
  412.               EVENTLIST_2nd(evl));
  413.     if (code != ERR)
  414.     code = value;
  415.     returnCode(code);
  416. }
  417. #endif
  418.  
  419. NCURSES_EXPORT(int)
  420. wgetch(WINDOW *win)
  421. {
  422.     int code;
  423.     unsigned long value;
  424.  
  425.     T((T_CALLED("wgetch(%p)"), win));
  426.     code = _nc_wgetch(win,
  427.               &value,
  428.               SP->_use_meta
  429.               EVENTLIST_2nd((_nc_eventlist *) 0));
  430.     if (code != ERR)
  431.     code = value;
  432.     returnCode(code);
  433. }
  434.  
  435. /*
  436. **      int
  437. **      kgetch()
  438. **
  439. **      Get an input character, but take care of keypad sequences, returning
  440. **      an appropriate code when one matches the input.  After each character
  441. **      is received, set an alarm call based on ESCDELAY.  If no more of the
  442. **      sequence is received by the time the alarm goes off, pass through
  443. **      the sequence gotten so far.
  444. **
  445. **    This function must be called when there are no cooked keys in queue.
  446. **    (that is head==-1 || peek==head)
  447. **
  448. */
  449.  
  450. static int
  451. kgetch(EVENTLIST_0th(_nc_eventlist * evl))
  452. {
  453.     struct tries *ptr;
  454.     int ch = 0;
  455.     int timeleft = ESCDELAY;
  456.  
  457.     TR(TRACE_IEVENT, ("kgetch() called"));
  458.  
  459.     ptr = SP->_keytry;
  460.  
  461.     for (;;) {
  462.     if (cooked_key_in_fifo() && SP->_fifo[head] >= KEY_MIN) {
  463.         break;
  464.     } else if (!raw_key_in_fifo()) {
  465.         ch = fifo_push(EVENTLIST_1st(evl));
  466.         if (ch == ERR) {
  467.         peek = head;    /* the keys stay uninterpreted */
  468.         return ERR;
  469.         }
  470. #ifdef NCURSES_WGETCH_EVENTS
  471.         else if (ch == KEY_EVENT) {
  472.         peek = head;    /* the keys stay uninterpreted */
  473.         return fifo_pull();    /* Remove KEY_EVENT from the queue */
  474.         }
  475. #endif
  476.     }
  477.  
  478.     ch = fifo_peek();
  479.     if (ch >= KEY_MIN) {
  480.         /* If not first in queue, somebody put this key there on purpose in
  481.          * emergency.  Consider it higher priority than the unfinished
  482.          * keysequence we are parsing.
  483.          */
  484.         peek = head;
  485.         /* assume the key is the last in fifo */
  486.         t_dec();        /* remove the key */
  487.         return ch;
  488.     }
  489.  
  490.     TR(TRACE_IEVENT, ("ch: %s", _tracechar((unsigned char) ch)));
  491.     while ((ptr != NULL) && (ptr->ch != (unsigned char) ch))
  492.         ptr = ptr->sibling;
  493.  
  494.     if (ptr == NULL) {
  495.         TR(TRACE_IEVENT, ("ptr is null"));
  496.         break;
  497.     }
  498.     TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d",
  499.               ptr, ptr->ch, ptr->value));
  500.  
  501.     if (ptr->value != 0) {    /* sequence terminated */
  502.         TR(TRACE_IEVENT, ("end of sequence"));
  503.         if (peek == tail)
  504.         fifo_clear();
  505.         else
  506.         head = peek;
  507.         return (ptr->value);
  508.     }
  509.  
  510.     ptr = ptr->child;
  511.  
  512.     if (!raw_key_in_fifo()) {
  513.         int rc;
  514.  
  515.         TR(TRACE_IEVENT, ("waiting for rest of sequence"));
  516.         rc = _nc_timed_wait(TWAIT_MASK,
  517.                 timeleft,
  518.                 &timeleft
  519.                 EVENTLIST_2nd(evl));
  520. #ifdef NCURSES_WGETCH_EVENTS
  521.         if (rc & 4) {
  522.         TR(TRACE_IEVENT, ("interrupted by a user event"));
  523.         /* FIXME Should have preserved timeleft for reusal... */
  524.         peek = head;    /* Restart interpreting later */
  525.         return KEY_EVENT;
  526.         }
  527. #endif
  528.         if (!rc) {
  529.         TR(TRACE_IEVENT, ("ran out of time"));
  530.         break;
  531.         }
  532.     }
  533.     }
  534.     ch = fifo_pull();
  535.     peek = head;
  536.     return ch;
  537. }
  538.